package com.baomidou.springwind.controller.city;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.kisso.annotation.Permission;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.springwind.baidu.RouteMatrix;
import com.baomidou.springwind.controller.BaseController;
import com.baomidou.springwind.entity.*;
import com.baomidou.springwind.enums.NodeTypeEnum;
import com.baomidou.springwind.pojo.*;
import com.baomidou.springwind.controller.NetworkNodeController;
import com.baomidou.springwind.service.CityOrderService;
import com.baomidou.springwind.service.NetworkNodeService;
import com.baomidou.springwind.service.ProjectService;
import com.baomidou.springwind.service.WeekOrderService;
import com.baomidou.springwind.utils.ParamMap;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 城市配送:订单分析
 * </p>
 *
 * @Author huangtao
 * @Date 2017-8-21
 */
@Controller
@RequestMapping("/network/city")
public class CostController extends BaseController {


    @Autowired
    private ProjectService projectService;
    @Autowired
    private CityOrderService cityOrderService;
    @Autowired
    private NetworkNodeService networkNodeService;
    @Autowired
    private RouteMatrix routeMatrix;

    private ExecutorService pool = Executors.newFixedThreadPool(10);
    /**
     * 成本主界面
     */
    @RequestMapping("/cost")
    @Permission("1001")
    public String order(Model model) {
        model.addAttribute("prjList", projectService.getUserAllProjects());
        return pjaxPath("/lpp/network/city/cost/main", model);
    }
    /**
     * 成本分析有4个成本
     * 中转仓成本
     * 配送回收成本
     * 总运输成本
     * 总成本
     */
    @ResponseBody
    @RequestMapping("/cost/bar1")
    @Permission("1001")
    public String  materialBar1() throws Exception{
        JSONObject ret1 = new JSONObject();
        ParamMap<String, Object> pm = getParameterMap();
        pm.put("isNumber",2);
        pm.put("days",30);
        pm.put("parameter",550);
        pm.put("coefficient",1.3);
        pm.put("distance1",5);
        pm.put("distance2",10);
        pm.put("lnglat",1);
        //中转仓成本
        Double ware=Double.valueOf(pm.get("wareCost").toString());
        //配送回收成本
        double worker=0;
        double workerCost=0;
        List<LinkedHashMap<String, Object>> cost1=getCityAll(pm);
        for (int i=0;i<cost1.size();i++){
            worker=worker+Double.valueOf(cost1.get(i).get("distribute").toString())+Double.valueOf(cost1.get(i).get("recovery").toString());
        }
        workerCost=worker*Double.valueOf(pm.get("workerCost").toString());
        //总运输成本
        double transportCost=0;
        JSONArray jsonArray=getVehicleCostData();
        JSONArray jsonArray1=new JSONArray();
        JSONArray jsonArray2=new JSONArray();
        int months=30;
        DecimalFormat df=new DecimalFormat(".##");
        for (int i=0;i<jsonArray.size();i++){
            transportCost=transportCost+Double.valueOf(jsonArray.get(i).toString());
            double double1=months*Double.valueOf(jsonArray.get(i).toString());
            jsonArray2.add(df.format(double1));
        }
        transportCost=transportCost*months;
        String tc=df.format(transportCost);
        //总成本
        double allCots=ware+workerCost+transportCost;
        ret1.put("ware",ware);
        ret1.put("workerCost",workerCost);
        ret1.put("transportCost",tc);
        ret1.put("allCots",df.format(allCots));
        ret1.put("s4",tc);
        ret1.put("s5",jsonArray2);
        ret1.putAll(getArray1(workerCost,ware,tc));
        ret1.putAll(getArray2(workerCost,ware,tc));
        return callbackSuccess(ret1);
    }
    private JSONObject getArray2(double workerCost,double ware,String transportCost){
        JSONObject ret = new JSONObject();
        JSONArray t = new JSONArray();
        //找到a1需要的数据
        JSONObject a1 = new JSONObject();
        JSONObject a2 = new JSONObject();
        JSONObject a3 = new JSONObject();
        JSONArray jsonArray=new JSONArray();
        a1.put("name","人工成本");
        a1.put("value",workerCost);
        jsonArray.add(a1);
        a2.put("name","中转仓成本");
        a2.put("value",ware);
        jsonArray.add(a2);
        a3.put("name","运输成本");
        a3.put("value",transportCost);
        jsonArray.add(a3);
        ret.put("s3",jsonArray);
        return ret;
    }
    private JSONObject getArray1(double workerCost,double ware,String transportCost){
        JSONObject ret1 = new JSONObject();
        JSONArray jsonArray1=new JSONArray();
        jsonArray1.add("人工成本");
        jsonArray1.add("中转仓成本");
        jsonArray1.add("运输成本");
        JSONArray jsonArray2=new JSONArray();
        jsonArray2.add(workerCost);
        jsonArray2.add(ware);
        jsonArray2.add(transportCost);
        ret1.put("s1",jsonArray1);
        ret1.put("s2",jsonArray2);
        return ret1;
    }
    private JSONArray getVehicleCostData(){
        JSONArray array = new JSONArray();
        EntityWrapper<NetworkNode> wrapper = new EntityWrapper<NetworkNode>();
        wrapper.eq("batch_id", getProjectId());
        wrapper.eq("node_type", NodeTypeEnum.CUSTOM.getType());
        wrapper.orderBy("node_type");
        wrapper.eq("operator_id", getCurrentUserId());
        List<NetworkNode> nodeList = networkNodeService.selectList(wrapper);
        CityTransportGroup group = new TransportGroupBuilder(nodeList).build();
        wrapper = new EntityWrapper<NetworkNode>();
        wrapper.eq("batch_id", getProjectId());
        wrapper.eq("node_type", NodeTypeEnum.DISTRIBUTE_CENTER.getType());
        NetworkNode dcNode = networkNodeService.selectOne(wrapper);
        array.add(String.format("%.2f ", groupDistance(dcNode, group.getCenterGroup())));
        array.add(String.format("%.2f ", groupDistance(dcNode, group.getFirstGroup())));
        array.add(String.format("%.2f ", groupDistance(dcNode, group.getSecondGroup())));
        array.add(String.format("%.2f ", groupDistance(dcNode, group.getThirdGroup())));
        array.add(String.format("%.2f ", groupDistance(dcNode, group.getFourthGroup())));
        Double otherOil = 0d;
        for (NetworkNode other : group.getOtherGroup()) {
            otherOil += groupDistance(dcNode, Arrays.asList(other));
        }
        array.add(String.format("%.2f ", otherOil));
        return array;
    }

    public List<LinkedHashMap<String, Object>> getCityAll(ParamMap<String, Object> pm){
        JSONObject rets=new JSONObject();
        //数据源
        HashMap<String, CityOrder> hm1 = getMap();
        List<Map.Entry<String,CityOrder>> citylist = new ArrayList<Map.Entry<String,CityOrder>>(hm1.entrySet());
        LinkedHashMap<String,CityDrivingRoute> cityDrivingRoute;
        //排序
        List<Map.Entry<String,CityOrder>> list = getlist(citylist);
        List<LinkedHashMap<String, Object>> ret1;
        double Rack=0;
        double RackNumber=0;
        String name=null;
        int m=Integer.valueOf(pm.get("isNumber").toString());//多少个数据不处理
        for (int i=0;i<m;i++){
            name=list.get(list.size()-i-1).getValue().getName();
            hm1.remove(name);
        }
        CityPeopleGroup cityPeopleGroup=new CityPeopleGroup();
        //第一次组合
        cityDrivingRoute = cityPeopleGroup.orderHashMap(hm1,pm);
        //加上剩余的组合
        for (int i=0;i<m;i++){
            CityDrivingRoute cityDrivingRoute1=new CityDrivingRoute(list.get(list.size()-i-1).getValue(),0,null,0,null);
            cityDrivingRoute.put(list.get(list.size()-i-1).getValue().getName(),cityDrivingRoute1);
        }
        ret1=cityPeopleGroup.orderCombination(cityDrivingRoute,pm);
        return ret1;
    }
    public HashMap<String, CityOrder> getMap(){
        EntityWrapper<CityOrder> wrapper = new EntityWrapper<CityOrder>();
        wrapper.eq("batch_id", getProjectId());
        wrapper.eq("operator_id", getCurrentUserId());
        final List<CityOrder> nodeList = cityOrderService.selectList(wrapper);
        HashMap<String, CityOrder> hm1=new HashMap<String, CityOrder>();
        for(int i=0;i<nodeList.size();i++){
            hm1.put(nodeList.get(i).getName(), nodeList.get(i));
        }
        return hm1;
    }
    public List<Map.Entry<String,CityOrder>> getlist(List<Map.Entry<String,CityOrder>> list){
        Collections.sort(list, new Comparator<Map.Entry<String,CityOrder>>() {
            @Override
            public int compare(Map.Entry<String,CityOrder> o1,Map.Entry<String,CityOrder> o2) {
                return o1.getValue().getOrderPositive().compareTo(o2.getValue().getOrderPositive());
            }
        });
        return list;
    }

    public JSONArray getTimes(NetworkNode dcNode,List<NetworkNode> nodes,Map<Long, TreeSet<RouteInfo>> routeMap,Map<Long, NetworkNode> nodeMap,JSONArray jsonArray,int i){
        List<VehicleBatch> vehicleBatchList = simulate(dcNode, nodes, routeMap);
        int i2=0;
        for (VehicleBatch batch : vehicleBatchList) {
            JSONObject object = new JSONObject();
            object.put("vehicle", batch.getVehicle());
            StringBuffer stringBuffer=new StringBuffer();
            i2++;
            stringBuffer.append("第").append(i).append("象限").append("第").append(i2).append("批次");
            for (Long nodeId : batch.getNodeList()) {
                NetworkNode node = nodeMap.get(nodeId);
            }
            object.put("customs", stringBuffer);
            Long startNodeId = dcNode.getId();
            Long duration = 0L;
            for (Long nodeId : batch.getNodeList()) {
                RouteInfo routeInfo = getRouteInfo(routeMap, startNodeId, nodeId);
                duration += routeInfo.duration;
                duration += (10 + 15) * 60;
                startNodeId = nodeId;
            }
            object.put("duration", duration);
            jsonArray.add(object);
        }
        return jsonArray;
    }


    private void addTimeVehicleMap(Map<Integer, Integer> timeMap, Map<Integer, Integer> groupTime) {
        for (Integer time : groupTime.keySet()) {
            if (timeMap.containsKey(time)) {
                timeMap.put(time, timeMap.get(time) + groupTime.get(time));
            } else {
                timeMap.put(time, groupTime.get(time));
            }
        }
    }

    private Double groupDistance(NetworkNode dcNode, List<NetworkNode> nodeList) {
        Map<Long, TreeSet<RouteInfo>> routeMap = getRutoInfoMap(dcNode, nodeList);
        Collections.sort(nodeList, new Comparator<NetworkNode>() {
            @Override
            public int compare(NetworkNode o1, NetworkNode o2) {
                return o2.getAmount().compareTo(o1.getAmount());
            }
        });
        List<VehicleBatch> vehicleBatchList = simulate(dcNode, nodeList, routeMap);
        Double oil = 0D;
        for (VehicleBatch batch : vehicleBatchList) {
            Long startNodeId = dcNode.getId();
            Long distance = 0L;
            for (Long nodeId : batch.getNodeList()) {
                RouteInfo routeInfo = getRouteInfo(routeMap, startNodeId, nodeId);
                distance += routeInfo.distance;
                startNodeId = nodeId;
            }
            oil += distance / 1000d * 1.5 * batch.getVehicle();
        }
        return oil;
    }


    private Map<Long, TreeSet<RouteInfo>> getRutoInfoMap(final NetworkNode dcNode, final List<NetworkNode> nodeList) {
        final Map<Long, TreeSet<RouteInfo>> routeMap = new LinkedHashMap<Long, TreeSet<RouteInfo>>();
        final CountDownLatch latch = new CountDownLatch(nodeList.size() + 1);
        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    TreeSet<RouteInfo> route = routeMatrix.getBaiduApiResult(dcNode, nodeList);
                    routeMap.put(dcNode.getId(), route);
                } finally {
                    latch.countDown();
                }
            }
        });
        for (final NetworkNode origins : nodeList) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        routeMap.put(origins.getId(), routeMatrix.getBaiduApiResult(origins, nodeList));
                    } finally {
                        latch.countDown();
                    }
                }
            });
        }
        try {
            latch.await(60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException("加载需求点距离异常");
        }
        return routeMap;
    }

    private Map<Integer, Integer> groupDuration(NetworkNode dcNode, List<NetworkNode> nodeList) {
        Map<Long, TreeSet<RouteInfo>> routeMap = getRutoInfoMap(dcNode, nodeList);
        List<VehicleBatch> vehicleBatchList = simulate(dcNode, nodeList, routeMap);
        Map<Integer, Integer> vehicleTime = new LinkedHashMap<Integer, Integer>();
        int line = 0;
        for (VehicleBatch batch : vehicleBatchList) {
            Long startNodeId = dcNode.getId();
            Long duration = 0L;
            for (Long nodeId : batch.getNodeList()) {
                RouteInfo routeInfo = getRouteInfo(routeMap, startNodeId, nodeId);
                duration += routeInfo.duration;
                duration += (10 + 15) * 60;
                startNodeId = nodeId;
            }
            int hours = Math.round(duration.floatValue() / 3600);
            for (int i = 1; i <= hours; i++) {
                vehicleTime.put(line + i, batch.getVehicle());
            }
            line = line + hours;
        }
        return vehicleTime;
    }

    private List<VehicleBatch> simulate(NetworkNode dcNode, List<NetworkNode> nodes, Map<Long, TreeSet<RouteInfo>> routeMap) {
        int batch = 2;
        int fullLoad = 15;
        int shelf = 550;
        int totalShelf = 0;
        for (NetworkNode node : nodes) {
            node.setShelf(new BigDecimal(node.getAmount().doubleValue() * 1.3 / 30 / shelf).setScale(1, RoundingMode.UP).doubleValue());
            totalShelf += node.getShelf();
        }
        int vehicle = (int) Math.ceil(totalShelf * 1.0f / fullLoad / batch);
        Map<Long, NetworkNode> finderMap = new HashMap<Long, NetworkNode>();
        for (NetworkNode node : nodes) {
            finderMap.put(node.getId(), node);
        }
        int vehicleAll = vehicle * fullLoad;
        boolean exclude = true;
        List<VehicleBatch> vehicleBatchList = new ArrayList<VehicleBatch>(batch);
        while (batch-- > 0) {
            VehicleBatch vBatch = new VehicleBatch();
            double amount = 0d;
            NetworkNode node = exclude ? nodes.get(0) : find(finderMap, routeMap, dcNode.getId());
            do {
                if (amount + node.getShelf() >= vehicleAll) {
                    node.setShelf(node.getShelf() - (vehicleAll - amount));
                    vBatch.addAmount(vehicleAll - amount);
                    if (node.getShelf() == 0) {
                        finderMap.remove(node.getId());
                    }
                    amount = vehicleAll;
                    vBatch.addNode(node.getId());
                    break;
                } else {
                    amount += node.getShelf();
                    vBatch.addAmount(node.getShelf());
                    finderMap.remove(node.getId());
                    exclude = false;
                }
                vBatch.addNode(node.getId());
                node = find(finderMap, routeMap, node.getId());
            } while (node != null);
            vBatch.setVehicle((int) Math.ceil(amount / fullLoad));
            vehicleBatchList.add(vBatch);
        }
        return vehicleBatchList;
    }


    private NetworkNode find(Map<Long, NetworkNode> finderMap, Map<Long, TreeSet<RouteInfo>> routeMap, Long nodeId) {
        NetworkNode node = null;
        TreeSet<RouteInfo> routeInfos = routeMap.get(nodeId);
        for (RouteInfo info : routeInfos) {
            node = finderMap.get(info.id);
            if (node != null) {
                break;
            }
        }
        return node;
    }

    private RouteInfo getRouteInfo(Map<Long, TreeSet<RouteInfo>> routeMap, Long start, Long end) {
        TreeSet<RouteInfo> routeInfos = routeMap.get(start);
        for (RouteInfo info : routeInfos) {
            if (end.equals(info.id)) {
                return info;
            }
        }
        return null;
    }

}
